#include <stdlib.h>
#include <string.h>
#include "fnv/fnv.h"
#include "pseHash.h"

using namespace std;

/* for 32bit environment */
#define FNV_32_PRIME ((unsigned int)16777619)
#define MAX_32BIT    ((unsigned int)0xffffffff) /* largest 32 bit unsigned value */

/* for 64bit environment */
#define FNV_64_PRIME       ((Fnv64_t)0x100000001b3ULL)
#define MAX_64BIT          ((Fnv64_t)0xffffffffffffffffULL) /* largest 64bit value */
#define FNV_64_PRIME_LOW   ((unsigned long)0x1b3)       /* lower bits of FNV prime */
#define FNV_64_PRIME_SHIFT (8)                /* top FNV prime shift above 2^32 */

#define RETRY        1
#define LAZY_MOD     2
#define HASH_LOCK_RD 1
#define HASH_LOCK_WR 2
#define HASH_LOCK_UN 3

/**
 * _hash_value() converts a input data into hash value.This is default hash
 * function.
 *
 * The str is a a NULL-terminated string.
 *
 * @param *hashtable the hash table which you created
 * @param *str       the input data which you want to store in hash table.
 * @param  str_len   length of 'str'
 * @return           return the hash value
 */
unsigned int _hash_value(hash_table_t *hashtable, char *str, size_t str_len);

/**
 * _fnv_hash_value() converts a input data into hash value.
 * generates a 32-bit or 64-bit hash value, using fnv.h.
 *
 * The detail of FNV hash sees official website.
 * http://www.isthe.com/chongo/tech/comp/fnv/#history
 *
 * @param  hashval        hashed value
 * @param  mapping_method designed for hash sizes that are a power of 2.
 * @param *hashtable      the hash table which you created
 * @param *str            the input data which you want to store in hash table.
 * @param  str_len        length of 'str'
 * @return                return the hash value
 */ 
/*
int _fnv_hash_value(Fnv32_t *hashval, unsigned int mapping_method,
                    hash_table_t *hashtable, char *str, size_t str_len);
*/
/**
 * _hash_lock() generates a lock and unlock mechanism for thread-safe of
 * hash.
 *
 * @param  self hash object
 * @param  opt  the option of read, write and unlock mechanism.
 * @return      if successes return 0, otherwise return -l;
 */
int _hash_lock(hash_table_t *self, int opt);


PseHashTable::PseHashTable(): hash_table(NULL), size(0), expired_seconds(0), memory_usage(0){     
    //unsigned int  i = 0;    
    /*allocate memory for the table structure*/
    /*
    if((hash_table = (hash_table_t *)malloc(sizeof(hash_table_t))) == NULL){
        //return -1;
    }
    hash_table->size = size;
    hash_table->docno = 0;    
    hash_table->item_counter = 0;
    */
    /*allocate memory for the table itself*/
    /*
    if(((hash_table->table = ((list_t **)malloc(sizeof(list_t *) * hash_table->size)))) == NULL){
        //return -1;
    }
    */
    /*initialize the elements of the table*/
    /*
    for( i = 0; i < hash_table->size; i++){
        hash_table->table[i] = NULL;        
    }
    */
}


PseHashTable::~PseHashTable() { 
 //memory_consumed -= sizeof(PseHashTable); 
  hash_free();
}

int _hash_lock(hash_table_t *self, int opt){
    assert(self != NULL);
    assert(opt >= 1 && opt <= 3);
    switch (opt) {
        case HASH_LOCK_RD: {
            if (pthread_mutex_lock(&(self->lock)) != 0) {
                fprintf(stderr, "hash read lock failed\n");
                return -1;
            }
            break;
        }
        case HASH_LOCK_WR: {
            if (pthread_mutex_lock(&(self->lock)) != 0) {
                fprintf(stderr, "hash write lock failed\n");
                return -1;
            }
            break;
        }
        case HASH_LOCK_UN : {
            if (pthread_mutex_unlock(&(self->lock)) != 0) {
                fprintf(stderr, "hash unlock failed\n");
                return -1;
            }
            break;
        }
        default : {
            fprintf(stderr, "unknown unlock failed\n");
            return -1;
        }
    }
    return 0;
}

int PseHashTable::hash_init(unsigned int hash_size){
    assert(hash_size > 0);
    unsigned int  i = 0;
    
    /*allocate memory for the table structure*/
    if((hash_table = (hash_table_t *)malloc(sizeof(hash_table_t))) == NULL){
        return -1;
    }
    hash_table->size = hash_size;
    hash_table->item_counter = 0;

    /*allocate memory for the table itself*/
    if(((hash_table->table = ((list_t **)malloc(sizeof(list_t *) * hash_table->size)))) == NULL){
        return -1;
    }
    /*initialize the elements of the table*/
    for( i = 0; i < hash_table->size; i++){
        hash_table->table[i] = NULL;        
    }
    size = hash_table->size;
    return 0;
}

unsigned int _hash_value(hash_table_t *hashtable, char *str, size_t str_len){
    assert(hashtable != NULL);
    assert(str != NULL);
    assert(str_len >= 0);
    unsigned int i = 0;
    unsigned int hashval = 0;
    for(i = 0; i < str_len; i++){
        hashval = *str + (hashval << 5) - hashval;
    }
    return  hashval % hashtable->size;
}

int _fnv_hash_value(Fnv32_t *hashval, unsigned int mapping_method,
                    hash_table_t *hashtable, char *str, size_t str_len){
    assert(mapping_method == 1 || mapping_method == 2);
    assert(hashtable != NULL);
    assert(str != NULL);
    assert(str_len >= 0);
    unsigned int RETRY_LEVEL = ((MAX_64BIT / hashtable->size) * hashtable->size);
    /**
     * Below is one of the non-powers of 2's method which is "Retry method".
     * An advantage of this method it avoids slightly biased in large values.
     * The disagvantage is that it requires additional caculations.
     *
     */
    if(mapping_method == RETRY){
        *hashval = fnv_32_buf((void *)str, str_len, FNV1_32_INIT);
        //*hashval = fnv_64_buf((void *)str, str_len, FNV1_64_INIT);
	while (*hashval >= RETRY_LEVEL) {
            *hashval = ((*hashval) * FNV_32_PRIME) + FNV1_32_INIT;
            //*hashval = ((*hashval) * FNV_64_PRIME) + FNV1_64_INIT;        
        }
        /* In Debian, it will cause "error: invalid lvalue in assignment" */
        //((unsigned int) *hashval) %= hashtable->size;
        *hashval = ((unsigned int)*hashval) % hashtable->size;
        return 0;
    }
    /**
     * Below is one of the non-powers of 2's method which is "Lazy mod mapping
     * method". It is faster than "RETRY", but it will produce some biases in
     * large values.
     */
    else if(mapping_method == LAZY_MOD){
    	*hashval = fnv_32_buf((void *)str, str_len, FNV1_32_INIT);
    	//*hashval = fnv_64_buf((void *)str, str_len, FNV1_64_INIT);
	*hashval = (unsigned int)(((unsigned int) *hashval) % hashtable->size);
        return 0;
    }
    return -1;
}

list_t *PseHashTable::hash_lookup(char *str, size_t str_len){                               	
    assert(str != NULL);
    assert(str_len >= 0);
    list_t *list = NULL;
    //unsigned int hashval = _hash_value(hashtable, str, str_len);
    //Fnv32_t hashval = 0;
    Fnv32_t hashval = 0;
    if(_fnv_hash_value(&hashval, LAZY_MOD, hash_table, str, str_len) == 0){       
        /*
        if(_hash_lock(hash_table, HASH_LOCK_WR) != 0){
            fprintf(stderr, "hash lookup lock failed\n");
            return NULL;
        }
        */
        for(list = hash_table->table[hashval]; list != NULL;
            list = list->next){            	
            /* compare the first byte first which is faster */
            //if(list->string != NULL){
            /* test the list_t object is empty or not */
            if(list->empty != true){	
                if(*str == *(list->string)) {
       	            if(str_len == list->string_len &&
                       strncmp(str, list->string, str_len) == 0){                        
                        /*
                        if(_hash_lock(hash_table, HASH_LOCK_UN) != 0){
                            fprintf(stderr, "hash lookup unlock failed\n");
                            return NULL;
                        }
                        */
                        return list;
                    }
                }
            }
            break;
        }
    }
    /*   
    if(_hash_lock(hash_table, HASH_LOCK_UN) != 0){
        fprintf(stderr, "hash lookup unlock failed\n");
        return NULL;
    }
    */        
    return NULL;
}

int PseHashTable::hash_add(char *str, size_t str_len, 
                           void *data, size_t data_len){
    assert(str != NULL);
    assert(str_len >= 0);
    list_t *new_list = NULL;
    list_t *current_list = NULL;
    //Fnv32_t hashval = 0;
    Fnv32_t hashval = 0;
    time_t  OldTimeOfAccess = 0;
    time_t  NewTimeOfAccess = 0;
    
    /*
    if(_hash_lock(hash_table, HASH_LOCK_WR) != 0){
        fprintf(stderr, "hash add lock failed\n");
        return -1;
    }
    */
    //unsigned int hashval = _hash_value(hash_table, str, str_len);
    if(_fnv_hash_value(&hashval, LAZY_MOD, hash_table, str, str_len) != 0){
        /*
	if(_hash_lock(hash_table, HASH_LOCK_UN) != 0){
            fprintf(stderr, "hash add lock failed\n");
            return -1;
        }
	*/
        return -1;
    }
    /* check whether the string is repeated or not */
    current_list = hash_lookup(str, str_len);
    if(current_list != NULL){
        current_list->match_count++;
        time(&NewTimeOfAccess);
        current_list->NewTimeOfAccess = NewTimeOfAccess;        
        current_list->age = current_list->NewTimeOfAccess - current_list->OldTimeOfAccess;
        free(current_list->data);
        current_list->data_len = 0;        
        current_list->data_len = data_len;
        //current_list->data = data;
	current_list->data = (void *)malloc(current_list->data_len + 1);
        memcpy(current_list->data, data, data_len);
        *((char *) current_list->data + current_list->data_len) = '\0';        
	/*
        current_list->data = (void *)strndup((char *)data, data_len);
        current_list->data_len = data_len;        
        */
	/*
        if(_hash_lock(hash_table, HASH_LOCK_UN) != 0){
            fprintf(stderr, "hash add lock failed\n");
            return -1;
        }
	*/
        return 0;
    }
    else{
        /* allocate memory for list */
        if((new_list = (list_t *)malloc(sizeof(list_t))) == NULL) {
            /*
	    if(_hash_lock(hash_table, HASH_LOCK_UN) != 0){
                fprintf(stderr, "hash add lock failed\n");
                return -1;
            }
	    */
    	    return -1;
        }
        /* insert into new list */
        new_list->match_count = 1;
        new_list->string = strndup(str, str_len);
        //new_list->string = strdup(str);
        new_list->string_len = str_len;
        new_list->data_len = data_len;        	
        //new_list->data = data;	    
	new_list->data = (void *)malloc(new_list->data_len + 1);
        memcpy(new_list->data, data, data_len);
        *((char *) new_list->data + new_list->data_len ) = '\0';
        /*
        new_list->data = (void *)strndup((char *)data, data_len);
        new_list->data_len = data_len;        
        */
        time(&OldTimeOfAccess);
        new_list->OldTimeOfAccess = OldTimeOfAccess;
        new_list->NewTimeOfAccess = 0;
        new_list->timeoffirst_access = 0;
        new_list->age = 0;
        new_list->empty = false;
        if(new_list->string) hash_table->item_counter++;
        new_list->next = hash_table->table[hashval];
        hash_table->table[hashval] = new_list;
        /*
	if(_hash_lock(hash_table, HASH_LOCK_UN) != 0){
            fprintf(stderr, "hash add lock failed\n");
            return -1;
        }
	*/
        return 0;
    }
    /*
    if(_hash_lock(hash_table, HASH_LOCK_UN) != 0){
        fprintf(stderr, "hash add lock failed\n");
        return -1;
    }
    */
    return -1;
}

int PseHashTable::hash_update(char *str, size_t str_len,
                           void *data, size_t data_len){                           	
    assert(str != NULL);
    assert(str_len >= 0);
    //Fnv32_t hashval = 0;
    /* write lock for thread-safe */
    /*
    if(_hash_lock(hash_table, HASH_LOCK_WR) != 0){
        fprintf(stderr, "hash update lock failed\n");
        return -1;
    }
    */
    list_t *current_list = hash_lookup(str, str_len);
    if(current_list != NULL){
        current_list->match_count++;
        time_t NewTimeOfAccess = 0;
        time(&NewTimeOfAccess);
        current_list->NewTimeOfAccess = NewTimeOfAccess;        
        current_list->age = current_list->NewTimeOfAccess - current_list->OldTimeOfAccess;                
        free(current_list->data);
        current_list->data_len = 0;        
        current_list->data_len = data_len;
        //current_list->data = data;
	current_list->data = (void *)malloc(current_list->data_len + 1);
        memcpy(current_list->data, data, data_len);
        *((char *) current_list->data + current_list->data_len ) = '\0';
	/*
        current_list->data = (void *)strndup((char *)data, data_len);
        current_list->data_len = data_len;
        */
        /*
        if(_hash_lock(hash_table, HASH_LOCK_UN) != 0){
            fprintf(stderr, "hash add lock failed\n");
            return -1;
        }
        */
        return 0;
    }    
    return -1;
}

int PseHashTable::hash_delete(char *str, size_t str_len){
    assert(str != NULL);
    assert(str_len >= 0);
    //Fnv32_t hashval = 0;
    Fnv32_t hashval = 0;
    list_t *current_list = NULL;
    list_t *next_list = NULL;
    
    next_list = (list_t *)malloc(sizeof(list_t *));
    /* check whether the string is repeated or not */    
    current_list = hash_lookup(str, str_len);    
    if(current_list == NULL){
        return -1;
    }
    /*   
    if(_hash_lock(hash_table, HASH_LOCK_WR) != 0){
        fprintf(stderr, "hash delete lock failed\n");
        return -1;
    }
    */    
    if(_fnv_hash_value(&hashval, LAZY_MOD, hash_table, str, str_len) == 0){
        /*
        if(_hash_lock(hash_table, HASH_LOCK_WR) != 0){
            fprintf(stderr, "hash delete lock failed\n");
            return -1;
        }
        */
        current_list = hash_table->table[hashval];
        if(current_list == NULL){
            return -1;
            /*
            if(_hash_lock(hash_table, HASH_LOCK_UN) != 0){
                fprintf(stderr, "hash delete unlock failed\n");
                return -1;
            }
            */        
        }
        next_list = current_list;
        current_list = current_list->next;        
        free(next_list->string);
        next_list->string = NULL;
        next_list->string_len = 0;
        free(next_list->data);
        next_list->data = NULL;
        next_list->data_len = 0;
        next_list->OldTimeOfAccess = 0;
        next_list->NewTimeOfAccess = 0; 
        next_list->timeoffirst_access = 0;
        next_list->age = 0;
        next_list->match_count = 0;
        next_list->empty = true;
        next_list = NULL;
        hash_table->item_counter--;
    }
    /*
    if(_hash_lock(hash_table, HASH_LOCK_UN) != 0){
        fprintf(stderr, "hash delete unlock failed\n");
        return -1;
    }
    */
    return 0;
}

int PseHashTable::hash_clear(){	
    assert(hash_table != NULL);
    list_t       *list = NULL;
    unsigned int  i = 0;
    if(_hash_lock(hash_table, HASH_LOCK_WR) != 0){
        fprintf(stderr, "hash delete lock failed\n");
        return -1;
    }
    for(i = 0; i < hash_table->size; i++){
        list = hash_table -> table[i];
        if(list != NULL){
            if(list->string != NULL){
                free(list->string);
                list->string = NULL;
                list->string_len = 0;
            }
            if(list->data != NULL){
                free(list->data);
                list->data = NULL;
                list->data_len = 0;
            }
            if(list->OldTimeOfAccess != 0){
                list->OldTimeOfAccess = 0;
            }
            if(list->NewTimeOfAccess != 0){
                list->NewTimeOfAccess = 0;
            }            
            if(list->timeoffirst_access != 0){
                list->timeoffirst_access = 0;
            }
            if(list->age != 0){
                list->age = 0;
            }            
            if(list->match_count != 0){
                list->match_count = 0;
            }
            if(list->empty != false){
                list->empty = true;
            }
            list = NULL;
            list = list->next;
        }
    }
    hash_table->item_counter = 0;
    if(_hash_lock(hash_table, HASH_LOCK_UN) != 0){
        fprintf(stderr, "hash delete unlock failed\n");
        return -1;
    }
    return 0;
}


int PseHashTable::hash_memory_usage(void){	
    int mem = sizeof(PseHashTable);
    mem += sizeof(list_t *) * size;
    for(unsigned int i = 0; i < size; i++) {
        list_t *list = hash_table -> table[i];
        while(list != NULL) {
	    mem += sizeof(list_t);
	    list = list->next;
	}
    }
    memory_usage = mem;
    return mem;
}

int PseHashTable::hash_free(){	
    unsigned int  i = 0;
    list_t       *list = NULL;
    list_t       *tmp = NULL;

    if(hash_table == NULL){
    	return -1;
    }
    /**
     * free the memory for every item in the table ,
     * including the strings themselves
     */
    for(i=0; i < hash_table->size; i++){
        list = hash_table -> table[i];
        while(list != NULL){
            tmp = list;
            list = list->next;
            /**
             * it will cause Program received signal SIGSEGV, Segmentation fault.
             * 0xdfac6836 in realfree () from /usr/lib/libc.so.1
             */
            //free(tmp->string);
            if(tmp->empty == false){
                free(tmp->string);
		tmp->string = NULL;
                tmp->string_len = 0;
		free(tmp->data);
                tmp->data = NULL;
                tmp->data_len = 0;
                tmp->match_count = 0;
                tmp->OldTimeOfAccess = 0;
                tmp->NewTimeOfAccess = 0;
                tmp->age = 0;
                tmp->timeoffirst_access = 0;            
            }
            //free(tmp);
        }
    }
    /* free the table itself */
    hash_table->size = 0;
    hash_table->item_counter = 0;
    if (pthread_mutex_destroy(&(hash_table->lock)) != 0) {
       fprintf(stderr, "mutex lock destroy failed\n");
       return 0;
    }
    //free(hash_table -> table);
    free(hash_table);
    return 0;
}

bool PseHashTable::hash_dump_file(const char *filename){
    assert(filename != NULL);
    unsigned int  i = 0;
    FILE         *fp = NULL;
    char          fp_buf[IO_BUF_SIZE];
    list_t       *list = NULL;
    list_t       *tmp = NULL;

    fp = fopen(filename,"wb");
    if(fp == NULL){
    	return false;
    	pse_hash_die(strerror(errno));
    }
    if (setvbuf(fp, fp_buf, _IOFBF, IO_BUF_SIZE) != 0) {
    	return false;
        pse_hash_die(strerror(errno));
    }
    /* hash_size area */
    fwrite(&(hash_table->size), sizeof(unsigned int), 1, fp);
    
    /* number of item in the hash table */    
    fwrite(&(hash_table->item_counter), sizeof(unsigned int), 1, fp);
    
    for(i = 0; i < hash_table->size; i++){
        list = hash_table -> table[i];
        while(list != NULL){
            tmp = list;
            list = list->next;
            if (fwrite(&(tmp->string_len),
                       sizeof(size_t), 1, fp) != 1) {
                    pse_hash_die("Error occurred while dumping the str_len of list_t");
            }
            if (fwrite(&(tmp->string),
                       sizeof(char ), tmp->string_len, fp) != tmp->string_len) {
                    pse_hash_die("Error occurred while dumping the string of list_t");
            }
            if (fwrite(&(tmp->data_len),
                       sizeof(size_t), 1, fp) != 1) {
                    pse_hash_die("Error occurred while dumping the str_len of list_t");
            }            
            if (fwrite(&(tmp->data),
                       sizeof(void *), tmp->data_len, fp) != tmp->data_len) {
                    pse_hash_die("Error occurred while dumping the data of list_t");
            }
            if (fwrite(&(tmp->OldTimeOfAccess),
                       sizeof(time_t), 1, fp) != 1) {
                    pse_hash_die("Error occurred while dumping the old time of access");
            }
            if (fwrite(&(tmp->NewTimeOfAccess),
                       sizeof(time_t), 1, fp) != 1) {
                    pse_hash_die("Error occurred while dumping the new time of access");
            }                        
            if (fwrite(&(tmp->timeoffirst_access),
                       sizeof(time_t), 1, fp) != 1) {
                    pse_hash_die("Error occurred while dumping the time of first access");
            }
            if (fwrite(&(tmp->age),
                       sizeof(time_t), 1, fp) != 1) {
                    pse_hash_die("Error occurred while dumping the time of first access");
            }            
            if (fwrite(&(tmp->match_count),
                       sizeof(int), 1, fp) != 1) {
                    pse_hash_die("Error occurred while dumping the match count of list_t");
            }
            if (fwrite(&(tmp->empty), sizeof(bool), 1, fp) != 1) {
                pse_hash_die("Error occurred while dumping empty arg");
            }
        }
    }
    if (fclose(fp) != 0) {
        pse_hash_die(strerror(errno));
    }
    return true;
}

/*
inline void fread_hash_table(hash_table_t *self, FILE *fp){
    
    if(fread(&self->string, sizeof(char), 1, fp) != 1) {
        pse_hash_die("Error occurred while loads string val");
    }
    if(fread(&string_len, sizeof(size_t), 1, fp) != 1) {
        pse_hash_die("Error occurred while loads str_len");
    }
    if(fread(&data, sizeof(void *), 1, fp) != 1) {
        pse_hash_die("Error occurred while loads data val");
    }
    if(fread(&OldTimeOfAccess, sizeof(time_t), 1, fp) != 1) {
        pse_hash_die("Error occurred while loads Old time of access");
    }
    if(fread(&NewTimeOfAccess, sizeof(time_t), 1, fp) != 1) {
        pse_hash_die("Error occurred while loads new time of access");
    }                
    if(fread(&timeoffirst_access, sizeof(time_t), 1, fp) != 1) {
        pse_hash_die("Error occurred while loads timeoffirst_access");
    }    
    if(fread(&match_count, sizeof(int), 1, fp) != 1) {
        if (feof(fp)) {
            break;
        }
        pse_hash_die("Error occurred while loads match count");
    }
}
*/

int PseHashTable::_hash_add_from_file(char *str, size_t str_len, void *data,
                                   time_t OldTimeOfAccess, time_t NewTimeOfAccess,
                                   time_t timeoffirst_access, time_t age, 
                                   int match_count, bool empty){
    assert(str != NULL);
    assert(str_len >= 0);
    assert(OldTimeOfAccess >= 0);
    assert(NewTimeOfAccess >= 0);
    assert(match_count >= 0);
    assert(timeoffirst_access >= 0);
    list_t *list = NULL;
    //Fnv32_t hashval = 0;
    Fnv32_t hashval = 0;

    if(_fnv_hash_value(&hashval, LAZY_MOD, hash_table, str, str_len) != 0){
        return -1;
    }
    if((list = (list_t *)malloc(sizeof(list_t))) == NULL) {
    	return -1;
    }
    if(list != NULL){       
        list->string = strdup(str);
        list->string_len = str_len;
        list->data = data;
        list->OldTimeOfAccess = OldTimeOfAccess;
        list->NewTimeOfAccess = NewTimeOfAccess;
        list->timeoffirst_access = timeoffirst_access;
        list->age = age;
        list->match_count = match_count;
        list->empty = empty;
        //list->next = &hash_table->table[hashval];
        //if(list->string) hash_table->item_counter++;
        list->next = hash_table->table[hashval];
        hash_table->table[hashval] = list;
        return 0;
    }
    return -1;
}

bool PseHashTable::hash_load_file(const char *filename){
    assert(filename != NULL);
    FILE   *fp = NULL;
    char    fp_buf[IO_BUF_SIZE] = "\0";
    unsigned int   item_counter = 0;
    unsigned int   hash_size = 0;
        
    fp = fopen(filename,"rb");
    if(fp == NULL){
    	return false;
    	//pse_hash_die(strerror(errno));
    }
    if(setvbuf(fp, fp_buf, _IOFBF, IO_BUF_SIZE) != 0) {
    	return false;
        //pse_hash_die(strerror(errno));
    }    
    /* load hash size */
    fread(&hash_size, sizeof(unsigned int), 1, fp);
    /*allocate memory for the hash table structure*/
    if(hash_init(hash_size) == -1){
        return false;
    } 
    /* load hash item_counter */
    fread(&item_counter, sizeof(unsigned int), 1, fp);
    hash_table->item_counter = item_counter;
    
    /* load the report of hash */
    /*
    int hit_count = 0;
    fread(&hit_count, sizeof(int), 1, fp);
    hash_table->hit_count = hit_count;
    
    
    int miss_count = 0;
    fread(&miss_count, sizeof(int), 1, fp);
    hash_table->miss_count = miss_count;
    
    int expired_hit_count = 0;
    fread(&expired_hit_count, sizeof(int), 1, fp);
    hash_table->expired_hit_count = expired_hit_count;
    */
    
    /*
    int docno = 0;
    fread(&docno, sizeof(unsigned int), 1, fp);
    hash_table->docno = docno;
    */
    
    for(unsigned int i = 0; i < hash_table->item_counter; i++){        
        while(hash_table -> table[i] != NULL){
            list_t *tmp = (list_t *)malloc(sizeof(list_t *));
            tmp = hash_table -> table[i];
            hash_table -> table[i] = hash_table -> table[i]->next;    
    	    if(fread(&(tmp->string_len), sizeof(size_t), 1, fp) != 1) {
                if (feof(fp)) {
                    break;
                }
                pse_hash_die("Error occurred while loads str_len");
            }
    	    if(tmp->string_len > 0){
    	        tmp->string = new char[tmp->string_len + 1];
    	        if(fread(&(tmp->string), sizeof(char ), tmp->string_len, fp) != tmp->string_len) {
                    pse_hash_die("Error occurred while loads string val");
                }
                tmp->string[tmp->string_len] = '\0';
            }
            if(fread(&(tmp->data_len), sizeof(size_t), 1, fp) != 1) {
                pse_hash_die("Error occurred while loads str_len");
            }
    	    if(fread(&(tmp->data), sizeof(void *), 1, fp) != 1) {
                tmp->data = (void *)new char[tmp->data_len + 1];
    	        if(fread(&(tmp->data), sizeof(char ), tmp->data_len, fp) != tmp->data_len) {
                    pse_hash_die("Error occurred while loads string val");
                }
                pse_hash_die("Error occurred while loads data val");
            }
    	    if(fread(&(tmp->OldTimeOfAccess), sizeof(time_t), 1, fp) != 1) {
                pse_hash_die("Error occurred while loads Old time of access");
            }
            if(fread(&(tmp->NewTimeOfAccess), sizeof(time_t), 1, fp) != 1) {
                pse_hash_die("Error occurred while loads new time of access");
            }                
            if(fread(&(tmp->timeoffirst_access), sizeof(time_t), 1, fp) != 1) {
                pse_hash_die("Error occurred while loads timeoffirst_access");
            }
    	    if(fread(&(tmp->age), sizeof(time_t), 1, fp) != 1) {
                pse_hash_die("Error occurred while loads timeoffirst_access");
            }               
    	    if(fread(&(tmp->match_count), sizeof(int), 1, fp) != 1) {
                pse_hash_die("Error occurred while loads match count");
            }
            if(fread(&(tmp->empty), sizeof(bool), 1, fp) != 1){
                pse_hash_die("Error occurred while load empty");
            }
        }
    }    
    if (fclose(fp) != 0) {
        pse_hash_die(strerror(errno));
    }
    return true;
}
       
bool PseHashTable::hash_search_in_file(const char *str,
                                    int str_len, const char *filename){
    assert(str != NULL);
    assert(str_len >= 0);
    assert(filename != NULL);
    FILE   *fp = NULL;
    char    fp_buf[IO_BUF_SIZE] = "\0";    
    //Fnv32_t hashval = 0;
    Fnv32_t hashval = 0;
//    int     offset = 0;
    unsigned int   item_counter = 0;
    unsigned int     i = 0;
    unsigned int   hash_size = 0;
    char   *string = NULL;          
    size_t  string_len = 0;      
    void   *data;
    time_t  OldTimeOfAccess = 0;
    time_t  NewTimeOfAccess = 0;
    time_t  timeoffirst_access = 0;          
    time_t  age = 0;
    int     match_count = 0;    
    bool    empty = true;

    fp = fopen(filename,"rb");
    if(fp == NULL){
    	return false;
    	//pse_hash_die(strerror(errno));
    }
    if(setvbuf(fp, fp_buf, _IOFBF, IO_BUF_SIZE) != 0) {
    	return false;
        //pse_hash_die(strerror(errno));
    }
    
    if(_fnv_hash_value(&hashval, LAZY_MOD, hash_table, (char *)str, str_len) != 0){
        return false;
    }
    rewind(fp);
        
    /* read the size of hash */
    fread(&hash_size, sizeof(unsigned int), 1, fp);
    /* read the item_counter arg of hash struct */
    fread(&item_counter, sizeof(unsigned int), 1, fp);
    
    /* read the report of hash */
    int hit_count = 0;
    fread(&hit_count, sizeof(int), 1, fp);
    
    int miss_count = 0;
    fread(&miss_count, sizeof(int), 1, fp);
    
    int expired_hit_count = 0;
    fread(&expired_hit_count, sizeof(int), 1, fp);
    
    int doc_no = 0;
    fread(&doc_no, sizeof(unsigned int), 1, fp);
    
    bool found = false;
    for(i = 0; i < item_counter; i++) {
        //fread_hash_node(node,fp,node_obj_read,the_save_mode);
    	if(fread(&string_len, sizeof(size_t), 1, fp) != 1) {
            if (feof(fp)) {
                break;
            }
            pse_hash_die("Error occurred while loads str_len");
        }
    	if(string_len > 0){
    	    string = new char[string_len + 1];
    	    if(fread(&string, sizeof(char ), string_len, fp) != string_len) {
            /*
            if (feof(fp)) {
                break;
            }
            */
                pse_hash_die("Error occurred while loads string val");
            }
            string[string_len] = '\0';
        }
        /*
        if(fread(&string, sizeof(char ), string_len, fp) != string_len) {
            pse_hash_die("Error occurred while loads string val");
        }
        */
    	if(fread(&data, sizeof(void *), 1, fp) != 1) {
            pse_hash_die("Error occurred while loads data val");
        }
    	if(fread(&OldTimeOfAccess, sizeof(time_t), 1, fp) != 1) {
            pse_hash_die("Error occurred while loads Old time of access");
        }
    	if(fread(&NewTimeOfAccess, sizeof(time_t), 1, fp) != 1) {
            pse_hash_die("Error occurred while loads new time of access");
        }                
    	if(fread(&timeoffirst_access, sizeof(time_t), 1, fp) != 1) {
            pse_hash_die("Error occurred while loads timeoffirst_access");
        }
    	if(fread(&age, sizeof(time_t), 1, fp) != 1) {
            pse_hash_die("Error occurred while loads timeoffirst_access");
        }               
    	if(fread(&match_count, sizeof(int), 1, fp) != 1) {
            pse_hash_die("Error occurred while loads match count");
        }
    	if(fread(&empty, sizeof(bool), 1, fp) != 1) {
            pse_hash_die("Error occurred while loads empty");
        }       
        if(*str == *(string)) {
       	    if(str_len == (int)strlen(string) &&
               strncmp(str, string, str_len) == 0){
                found = true;
                break;
            }
            
            else{
                found = false;
                break;
            }
            
        }
    }
    if(found == true){
        fprintf(stderr, "searching %s match %s item\n", str, string);
    }
    return found;
}
